/*
 *    Copyright (c) 2018-2025, lengleng All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the wrench4cloud.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: lengleng (wangiegie@gmail.com)
 */
package cn.mawenda.docker.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import cn.mawenda.docker.entity.ContainerMonitorLog;
import cn.mawenda.docker.mapper.ContainerMonitorLogMapper;
import cn.mawenda.docker.service.ContainerMonitorLogService;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.async.ResultCallback;
import com.github.dockerjava.api.command.StatsCmd;
import com.github.dockerjava.api.model.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.Closeable;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 容器监控统计日志记录表
 *
 * @author Ma.wenda
 * @date 2024-05-16 15:10:45
 */
@Service
@Slf4j
public class ContainerMonitorLogServiceImpl extends ServiceImpl<ContainerMonitorLogMapper, ContainerMonitorLog> implements ContainerMonitorLogService {

    @Resource
    DockerService dockerService;

    @Override
    public void containerMonitorStatistics(String containerName) {

        LocalDateTime localDateTime = LocalDateTime.now();

        log.info("containerMonitorStatistics start. containerName:{}, localDateTime:{}", containerName, localDateTime);


        DockerClient dockerClient = dockerService.dockerClient();

        // 获取容器ID
        String containerId = null;

        List<Container> containers = dockerClient.listContainersCmd().exec();

        for (Container container : containers) {
            if (Arrays.asList(container.getNames()).contains("/" + containerName)) {
                containerId = container.getId();
                break;
            }
        }

        if (containerId == null) {
            log.error("containerMonitorStatistics end. containerId is null, containerName:{}", containerName);
            throw new RuntimeException("Container not found.");
        }


        log.info("containerMonitorStatistics start. containerId:{}, containerName:{}", containerId, containerName);
        StatsCmd statsCmd = dockerClient.statsCmd(containerId);

        String finalContainerId = containerId;
        statsCmd.exec(new ResultCallback<Statistics>() {
            @Override
            public void close() throws IOException {

            }

            @Override
            public void onStart(Closeable closeable) {

            }

            @Override
            public void onNext(Statistics statistics) {
                log.info("containerMonitorStatistics statistics get. containerId:{}, containerName:{}, localDateTime:{}", finalContainerId, containerName, localDateTime);
                if(Objects.nonNull(statistics)){
                    ContainerMonitorLog containerMonitorLog = new ContainerMonitorLog();

                    containerMonitorLog.setContainerCode(finalContainerId);
                    containerMonitorLog.setContainerName(containerName);
                    containerMonitorLog.setCreateTime(localDateTime);

                    // 计算CPU占用率
                    CpuUsageConfig cpuUsage = statistics.getCpuStats().getCpuUsage();
                    if(Objects.nonNull(cpuUsage)){
                        double totalUsage = cpuUsage.getTotalUsage();
                        log.info("containerMonitorStatistics cpuUsage, containerName:{}, totalUsage:{}", containerName, totalUsage);
                        double usageInKernelmode = cpuUsage.getUsageInKernelmode();
                        log.info("containerMonitorStatistics cpuUsage, containerName:{}, usageInKernelmode:{}", containerName, usageInKernelmode);
                        double cpuUsagePercentage = 0;
                        if(totalUsage > 0 && usageInKernelmode > 0){
                            cpuUsagePercentage = (usageInKernelmode / totalUsage) * 100;
                        }
                        containerMonitorLog.setCpuUsage(cpuUsagePercentage);
                    }

                    // 计算内存使用率
                    MemoryStatsConfig memoryStats = statistics.getMemoryStats();
                    if(Objects.nonNull(memoryStats)){
                        double usage = memoryStats.getUsage();
                        log.info("containerMonitorStatistics memoryStats, containerName:{}, usage:{}", containerName, usage);
                        double limit = memoryStats.getLimit();
                        log.info("containerMonitorStatistics memoryStats, containerName:{}, limit:{}", containerName, limit);
                        double memoryUsagePercentage = 0;
                        if(usage > 0 && limit > 0){
                            memoryUsagePercentage = (usage / limit) * 100;
                        }
                        containerMonitorLog.setMemoryUsage(memoryUsagePercentage);
                    }

                    // 获取最新网络IO数据
                    Map<String, StatisticNetworksConfig> networks = statistics.getNetworks();
                    if(!CollectionUtils.isEmpty(networks)){
                        StatisticNetworksConfig statisticNetworksConfig = networks.get("eth0");
                        if(Objects.nonNull(statisticNetworksConfig)){
                            double rxBytes = (double)statisticNetworksConfig.getRxBytes() / (double)1000;
                            log.info("containerMonitorStatistics networks, containerName:{}, rxBytes:{}", containerName, rxBytes);
                            double txBytes = (double)statisticNetworksConfig.getTxBytes() / (double)1000;
                            log.info("containerMonitorStatistics networks, containerName:{}, txBytes:{}", containerName, txBytes);
                            containerMonitorLog.setNetworkIo(rxBytes + ":" + txBytes);
                        }
                    }

                    log.info("containerMonitorStatistics containerId:{}, containerName:{}, localDateTime:{}, \n containerMonitorLog:{}", finalContainerId, containerName, localDateTime, containerMonitorLog);
                    baseMapper.insert(containerMonitorLog);
                }
                try {
                    dockerClient.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }

            @Override
            public void onError(Throwable throwable) {

            }

            @Override
            public void onComplete() {

            }
        });
    }

}